1 module hip.api.data.commons; 2 public import hip.api.data.asset; 3 import hip.util.reflection; 4 5 6 ///Use @Asset instead of HipAsset. 7 pragma(LDC_no_typeinfo) 8 struct HipAssetUDA(T, Extra) 9 { 10 string path; 11 static if(!(is(T == void))) 12 T function(string data) conversionFunction; 13 static if(!(is(Extra == void))) 14 Extra extra; 15 int start, end; 16 } 17 18 /** 19 * Params: 20 * path = Path where the asset is located. 21 It may receive an $ for path formatting with numbers(only valid when array is used.) 22 * conversionFunc = A function with input the data located at "path", and return any data. 23 * start = For Arrays. Inclusive. May be greater than end for reverse counting. 24 * end = For Arrays. Inclusive. 25 * Returns: 26 */ 27 HipAssetUDA!(T, void) Asset(T)(string path, T function(string) conversionFunc, int start = 0, int end = 0){return HipAssetUDA!(T, void)(path, conversionFunc, start, end);} 28 29 /** 30 * Params: 31 * path = Path where the asset is located. 32 It may receive an $ for path formatting with numbers(only valid when array is used.) 33 * conversionFunc = A function with input the data located at "path", and return any data. 34 * extra = Extra data for instantiating the asset. Usually associated with the constructor 35 * start = For Arrays. Inclusive. May be greater than end for reverse counting. 36 * end = For Arrays. Inclusive. 37 * Returns: 38 */ 39 HipAssetUDA!(T, Extra) Asset(T, Extra)(string path, T function(string) conversionFunc, Extra extra, int start = 0, int end = 0){return HipAssetUDA!(T, Extra)(path, conversionFunc, extra, start, end);} 40 41 /** 42 * Params: 43 * path = Path where the asset is located. 44 It may receive an $ for path formatting with numbers(only valid when array is used.) 45 * Returns: 46 */ 47 HipAssetUDA!(void, void) Asset(string path){return HipAssetUDA!(void, void)(path, 0, 0);} 48 /** 49 * Params: 50 * path = Path where the asset is located. 51 It may receive an $ for path formatting with numbers(only valid when array is used.) 52 * start = For Arrays. Inclusive. May be greater than end for reverse counting. 53 * end = For Arrays. Inclusive. 54 * Returns: 55 */ 56 HipAssetUDA!(void, void) Asset(string path, int start, int end){return HipAssetUDA!(void, void)(path, start, end);} 57 58 /** 59 * Params: 60 * path = Path where the asset is located. 61 It may receive an $ for path formatting with numbers(only valid when array is used.) 62 * extra = Extra data for instantiating with the asset. Usually used with its constructor 63 * start = For Arrays. Inclusive. May be greater than end for reverse counting. 64 * end = For Arrays. Inclusive. 65 * Returns: 66 */ 67 HipAssetUDA!(void, T) Asset(T)(string path, T extra, int start = 0, int end = 0) if(!isFunction!T) {return HipAssetUDA!(void, T)(path, extra, start, end);} 68 69 70 template FilterAsset(Attributes...) 71 { 72 import std.traits:isInstanceOf; 73 import std.meta:AliasSeq; 74 static foreach(attr; Attributes) 75 static if(isInstanceOf!(HipAssetUDA, typeof(attr))) 76 alias FilterAsset = attr; 77 } 78 79 template GetAssetUDA(Attributes...) 80 { 81 alias asset = FilterAsset!(Attributes); 82 static if(!is(typeof(asset) == void)) //Means it is a real struct. 83 enum GetAssetUDA = asset; 84 else 85 enum GetAssetUDA = HipAssetUDA!(void, void)(); 86 } 87 88 89 public string[] splitLines(string input) 90 { 91 string[] ret; 92 size_t lastCut = 0; 93 foreach(i, ch; input) 94 { 95 if(ch == '\n') 96 { 97 ret~= input[lastCut..i]; 98 lastCut = i+1; 99 } 100 } 101 if(lastCut < input.length) ret~= input[lastCut..$]; 102 return ret; 103 } 104 105 106 string[] getModulesFromRoot(string modules, string root) 107 { 108 string[] ret = splitLines(modules); 109 110 ptrdiff_t rootStart = -1; 111 foreach(i, mod; ret) 112 { 113 if(mod.length < root.length) 114 { 115 if(rootStart == -1) 116 continue; 117 else 118 return ret[rootStart..i]; 119 120 } 121 if(mod[0..root.length] == root) 122 { 123 if(rootStart == -1) 124 rootStart = i; 125 } 126 else if(rootStart != -1) 127 return ret[rootStart..i]; 128 } 129 assert(rootStart != -1, "Unable to find root "~root~" in modules list."); 130 return ret[rootStart..$]; 131 } 132 133 IHipAssetLoadTask[] loadAssets()(TypeInfo type, string assetPath, const(ubyte)[] extraData, int start, int end) 134 { 135 import hip.api; 136 int sign = end - start >= 0 ? 1 : -1; 137 ///Include 1 for the upper bounds 138 int count = ((end - start) * sign) + 1; 139 if(count == 1) return [HipAssetManager.loadAsset(type, assetPath, extraData)]; 140 IHipAssetLoadTask[] ret = new IHipAssetLoadTask[count]; 141 142 static string formatStr(string str, int number) 143 { 144 import hip.util.to_string_range; 145 char[32] numSink = 0xff; 146 char[] data = numSink; 147 toStringRange(data, number); 148 int charCount = 0; 149 while(numSink[charCount++] != 0xff){} charCount--; 150 //-1 for the $ 151 char[] formattedStr = new char[(cast(int)str.length)-1+charCount]; 152 int i = 0; 153 foreach(ch; str) 154 { 155 if(ch == '$') 156 formattedStr[i..i+=charCount] = numSink[0..charCount]; 157 else 158 formattedStr[i++] = ch; 159 } 160 return cast(string)formattedStr; 161 } 162 163 foreach(i; 0..count) 164 ret[i] = HipAssetManager.loadAsset(type, formatStr(assetPath, start+i*sign), extraData); 165 return ret; 166 } 167 168 mixin template LoadAllAssets(string modules) 169 { 170 import hip.api.data.commons; 171 mixin LoadReferencedAssets!(splitLines(modules)); 172 } 173 mixin template LoadReferencedAssets(string[] modules) 174 { 175 //TODO: Improve that loadReferenced to a better 176 void loadReferenced() 177 { 178 import std.stdio; 179 static foreach(modStr; modules) 180 {{ 181 mixin("import ",modStr,";"); 182 alias theModule = mixin(modStr); 183 static foreach(moduleMemberStr; __traits(allMembers, theModule)) 184 {{ 185 alias moduleMember = __traits(getMember, theModule, moduleMemberStr); 186 static if(is(moduleMember type) && (is(type == class) || is(type == struct))) 187 { 188 static foreach(classMemberStr; __traits(derivedMembers, type)) 189 {{ 190 alias classMember = __traits(getMember, type, classMemberStr); 191 alias assetUDA = GetAssetUDA!(__traits(getAttributes, classMember)); 192 // pragma(msg, assetUDA); 193 static if(assetUDA.path !is null) 194 {{ 195 import hip.util.reflection: isArray; 196 197 static if(!is(typeof(classMember) == string) && isArray!(typeof(classMember))) alias memberType = typeof(classMember.init[0]); 198 else alias memberType = typeof(classMember); 199 200 201 const(ubyte)[] extra; 202 static if(__traits(hasMember, assetUDA, "extra")) 203 { 204 auto v = assetUDA.extra; 205 extra = (cast(ubyte*)&v)[0..typeof(v).sizeof]; 206 207 } 208 IHipAssetLoadTask[] tasks = loadAssets(typeid(memberType), assetUDA.path, extra, assetUDA.start, assetUDA.end); 209 memberType* members; 210 211 static if(!__traits(compiles, classMember.offsetof)) //Static 212 { 213 static if(!is(memberType == string) && isArray!(typeof(classMember))) 214 { 215 size_t start = classMember.length; 216 classMember.length+= tasks.length; 217 members = &classMember[start]; 218 } 219 else 220 members = &classMember; 221 222 foreach(i, task; tasks) 223 { 224 static if(__traits(hasMember, assetUDA, "conversionFunction")) 225 task.into(assetUDA.conversionFunction, &members[i]); 226 else static if(is(memberType == string)) 227 task.into(&members[i]); 228 else 229 task.into!(memberType)(&members[i]); 230 } 231 } 232 }} 233 }} 234 } 235 }} 236 }} 237 } 238 } 239 240 241 ///foreachAsset: void foreachAsset(T)(string assetPath) 242 mixin template ForeachAssetInClass(T, alias foreachAsset) 243 { 244 void ForeachAssetInClass() 245 { 246 import std.traits:isFunction; 247 static foreach(member; __traits(derivedMembers, T)) 248 {{ 249 alias theMember = __traits(getMember, T, member); 250 static if(!isFunction!theMember) 251 { 252 alias type = typeof(theMember); 253 enum assetUDA = GetAssetUDA!(__traits(getAttributes, theMember)); 254 static if(assetUDA.path != null) 255 { 256 const(ubyte)[] extra; 257 static if(__traits(hasMember, assetUDA, "extra")) 258 { 259 const v = assetUDA.extra; 260 extra = (cast(const(ubyte*))&v)[0..typeof(v).sizeof]; 261 } 262 static if(__traits(isTemplate, foreachAsset)) 263 foreachAsset!(type, theMember)(assetUDA.path, extra); 264 else 265 foreachAsset(assetUDA.path, extra); 266 } 267 } 268 }} 269 } 270 } 271 272 mixin template PreloadAssets() 273 { 274 private void _load(alias theMember)(TypeInfo t, string assetPath, const(ubyte)[] extraData) 275 { 276 import hip.api; 277 HipAssetManager.loadAsset(t, assetPath, extraData).into(&theMember); 278 } 279 alias preload = ForeachAssetInClass!(typeof(this), _load); 280 } 281 282 /** 283 * Usage: 284 ```d 285 class SomeScene : IHipPreloadable 286 { 287 mixin Preload; ///IHipPreloadable lets you use Preload symbol. 288 289 ///Will load "someTexture.png" inside the member 'texture' 290 @Asset("someTexture.png") 291 IHipTexture texture; 292 293 ///Loads game levels inside this variable 294 @Asset("gameLevels.txt", &parseGameLevels) 295 GameLevel[] gameLevels 296 297 ///Doesn't need to call 'preload()' to populate. As it is variable, it will be populated right after its load. 298 @Asset("helpText.txt") 299 static string helpText; 300 301 void initialize() 302 { 303 preload(); ///Needed to call for populating your assets after the class creation 304 } 305 306 GameLevel[] parseGameLevels(string data){return [];} 307 } 308 ``` 309 */ 310 interface IHipPreloadable 311 { 312 void preload(); 313 string[] getAssetsForPreload(); 314 315 mixin template Preload() 316 { 317 mixin template finalImpl() 318 { 319 private __gshared string[] _assetsForPreload; 320 private __gshared void getAsset(string asset, const(ubyte)[] extraData){_assetsForPreload~= asset;} 321 private final void loadAsset(T, alias member)(string asset, const(ubyte)[] extraData) 322 { 323 alias mem = member; 324 ///Take members that aren't static and populate them after loading. 325 static if(__traits(compiles, mem.offsetof)) 326 { 327 ///Try converting the member with conversion function 328 static if(!__traits(compiles, HipAssetManager.get!T)) 329 { 330 alias assetUDA = GetAssetUDA!(__traits(getAttributes, mem)); 331 static assert(__traits(hasMember, assetUDA, "conversionFunction"), 332 "Type has no conversion function and HipAssetManager can't infer its type."); 333 mem = assetUDA.conversionFunction(HipAssetManager.get!string(asset)); 334 } 335 else //Just get from asset manager 336 mem = HipAssetManager.get!T(asset); 337 } 338 } 339 } 340 mixin template impl() 341 { 342 string[] getAssetsForPreload() 343 { 344 if(_assetsForPreload.length == 0) 345 { 346 mixin ForeachAssetInClass!(typeof(this), __traits(child, this, getAsset)) f; 347 f.ForeachAssetInClass; 348 } 349 return _assetsForPreload; 350 } 351 void preload() 352 { 353 mixin ForeachAssetInClass!(typeof(this), loadAsset) f; 354 f.ForeachAssetInClass; 355 } 356 } 357 358 359 ///Deal with override/no override 360 mixin finalImpl; 361 static if(__traits(compiles, typeof(super).preload)){override: mixin impl;} 362 else{mixin impl;} 363 } 364 } 365 366 /** 367 * OpenGL Renderer must implement IReloadable for when changing device orientation. 368 */ 369 interface IReloadable 370 { 371 bool reload(); 372 } 373 374 375 376 enum HipAssetResult : ubyte 377 { 378 waiting, 379 cantLoad, 380 loading, 381 mainThreadLoading, 382 loaded 383 } 384 385 /** 386 * IHipAssetLoadTask is the base return type from any asset you want to `HipAssetManager.load{X}`. 387 * The loading, unless otherwise stated, is asynchronous. For simple games, most of the time you won't need 388 * to directly used LoadTask as currently the engine loads all the assets at startup to make it easier 389 * to prototype a game without needing to think about those tasks. 390 * 391 * `await` is not supported on WebAssembly export, so, don't use it if you plan to export to web. 392 */ 393 interface IHipAssetLoadTask 394 { 395 HipAssetResult result() const; 396 ///Sets the result. Should not exist in user code. 397 HipAssetResult result(HipAssetResult result); 398 399 HipAsset asset(); 400 ///Sets the asset. Should not exist in user code. 401 HipAsset asset(HipAsset asset); 402 403 bool hasFinishedLoading() const; 404 ///Awaits the asset load process. Can't be used on WebAssembly export 405 void await(); 406 ///When the variables finish loading, it will also assign the asset to the variables 407 void into(void* function(HipAsset asset) castFunc, HipAsset*[] variables...); 408 final void into(T)(T*[] variables...) 409 { 410 into((HipAsset asset) => (cast(void*)cast(T)asset), cast(HipAsset*[])variables); 411 } 412 void into(string*[] variables...); 413 414 ///May be executed instantly if the asset is already loaded. 415 void addOnCompleteHandler(void delegate(HipAsset) onComplete); 416 void addOnCompleteHandler(void delegate(string) onComplete); 417 ///Executs a step on the loading task. Call `asset` when state is loaded 418 void update(); 419 final void into(T)(T function(string) convertFunction, T*[] variables...) 420 { 421 T*[] vars = variables.dup; 422 addOnCompleteHandler((string data) 423 { 424 foreach(v; vars) 425 *v = convertFunction(data); 426 }); 427 } 428 } 429 430 interface IHipDeserializable 431 { 432 IHipDeserializable deserialize(string data); 433 IHipDeserializable deserialize(void* data); 434 }